Skip to content

fix(rbac): block preview public channels - #2713

Merged
riderx merged 15 commits into
mainfrom
codex/guard-app-preview-public-channel
Aug 8, 2026
Merged

fix(rbac): block preview public channels#2713
riderx merged 15 commits into
mainfrom
codex/guard-app-preview-public-channel

Conversation

@riderx

@riderx riderx commented Jul 17, 2026

Copy link
Copy Markdown
Member

Summary

  • Require app.update_settings to create a public/default channel.
  • Keep App Preview keys able to create private PR channels and atomically upload/promote their bundles.
  • Enforce the same boundary for direct CLI table writes and the raw-SQL create-and-promote path.

RLS execution model

  • Surface: public.channels INSERT WITH CHECK; it runs once per inserted channel row from the CLI/PostgREST path.
  • Existing app.create_channel remains required for every new channel.
  • A row with public = true additionally requires the existing, caller-scoped app.update_settings RBAC check on the row owner app. App Preview does not receive that permission.
  • The policy adds no tables, joins, or new helper functions. It uses the existing RBAC request helper with the insert row indexed owner_org and app_id values.
  • The endpoint atomic create-and-promote transaction uses a direct PostgreSQL client and therefore bypasses RLS; it performs the matching app.update_settings guard before inserting.

EXPLAIN and integration follow-up

  • Local EXPLAIN (ANALYZE, BUFFERS) and the affected lifecycle integration test are pending: the local Supabase runtime cannot start because Docker/Dory is unavailable (Cannot connect to .../.dory/dory.sock).
  • Before this leaves draft, collect policy-plan evidence for App Preview non-public insert, App Preview public insert denial, and an app-admin public insert. CI must also pass the isolated lifecycle test.

Validation

  • bun lint
  • bun lint:backend
  • Focused ESLint for changed tests
  • bun run typecheck:backend
  • bunx vitest run tests/channel-post.unit.test.ts - 16 passed
  • bun test:unit - 160 files / 1,065 tests passed

Note

Medium Risk
Changes authorization for public channel creation and promotion across RLS, triggers, and the API—delivery-sensitive RBAC—but behavior is narrowly scoped with broad test coverage.

Overview
App Preview and channel-scoped keys can no longer create or promote public/default channels without app.update_settings. Private preview channels and normal channel settings updates stay allowed under existing permissions.

Database RLS on channels INSERT now requires app.update_settings when public is true, while UPDATE policies remain channel-scoped so admins can still edit already-public channels. A BEFORE UPDATE trigger on public enforces the same rule for private → public flips on direct PostgREST/CLI writes.

The POST /channel handler loads existing public state and applies the same publicizing check before create/update. pgTAP, unit, and CLI integration tests cover preview denial, channel-admin promotion denial, and unchanged edits on public channels.

Reviewed by Cursor Bugbot for commit 3684d36. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Added stronger permission checks for creating public channels and promoting private channels.
    • App-level settings permissions are now required to make channels public.
    • Channel administrators can continue managing permitted channel settings without changing visibility.
  • Bug Fixes

    • Prevented unauthorized public channel creation and promotion, including through preview workflows.
    • Already-public channels remain manageable according to channel-level permissions.
    • Permission-denial messages now include relevant app and channel details.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Public channel creation and private-to-public promotion now require app.update_settings. Database policies, triggers, endpoint checks, CLI tests, unit tests, and pgTAP tests cover the authorization rules.

Changes

Public channel authorization

Layer / File(s) Summary
Database authorization guards
supabase/migrations/20260808175552_app_preview_public_channel_guard.sql, supabase/tests/26_test_rls_policies.sql, supabase/tests/67_test_public_channel_guard.sql
Channel inserts and private-to-public updates now require app settings permission. Channel-scoped updates remain available for already-public channels.
Endpoint permission checks
supabase/functions/_backend/public/channel/post.ts, tests/channel-post.unit.test.ts
The endpoint reads existing channel visibility and checks app settings permission when creating or publicizing a channel. Unit tests cover denied and allowed updates.
CLI and preview lifecycle validation
tests/cli-preview-lifecycle.test.ts, tests/cli-channel.test.ts
CLI tests verify rejected public channel creation, default-channel protection, channel isolation, and persistence behavior.

Onboarding metric typing

Layer / File(s) Summary
Metric row typing
supabase/functions/_backend/triggers/logsnag_insights.ts
AppBuildOnboardingMetricRow accepts additional string-keyed properties with unknown values.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ChannelRequest
  participant ChannelsRLS
  participant PublicTransitionTrigger
  participant ChannelsTable
  ChannelRequest->>ChannelsRLS: Insert or update public channel
  ChannelsRLS->>PublicTransitionTrigger: Check private-to-public transition
  PublicTransitionTrigger->>ChannelsTable: Allow or reject mutation
Loading

Possibly related PRs

Suggested labels: codex

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the primary RBAC change that blocks preview channels from becoming public.
Description check ✅ Passed The description clearly explains the change, implementation, validation results, pending checks, and testing limitations.

Comment @coderabbitai help to get the list of available commands.

@codspeed-hq

codspeed-hq Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing codex/guard-app-preview-public-channel (3684d36) with main (6b08031)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@riderx
riderx marked this pull request as ready for review July 27, 2026 11:27
@cursor

cursor Bot commented Jul 27, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_577ec601-13f5-4795-991d-aa88a224e618)

@cursor

cursor Bot commented Jul 27, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_fa0907f3-988c-4aab-8f1d-9a7834fd8632)

@coderabbitai coderabbitai Bot added the codex label Jul 27, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed against the latest diff

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread supabase/functions/_backend/public/channel/post.ts Outdated
@riderx
riderx marked this pull request as draft July 28, 2026 11:13
@riderx
riderx marked this pull request as ready for review July 28, 2026 11:20
@cursor

cursor Bot commented Jul 28, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_ab96f4b2-26d5-4bc9-ba2d-e0ed44955c91)

@riderx

riderx commented Jul 28, 2026

Copy link
Copy Markdown
Member Author

@cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented Jul 28, 2026

Copy link
Copy Markdown

@cubic-dev-ai review

@riderx I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 4 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

@riderx
riderx marked this pull request as draft July 28, 2026 11:36
Co-authored-by: Cursor <cursoragent@cursor.com>
@riderx
riderx marked this pull request as ready for review August 8, 2026 17:18
@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_68f59008-fe23-4ba5-9dba-bae8401d1f42)

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 4 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

cursoragent and others added 2 commits August 8, 2026 17:45
Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Keep channel UPDATE RLS channel-scoped so already-public edits still work.
Require app.update_settings only on private-to-public transitions via a
BEFORE UPDATE trigger, and align SQL/CLI isolation tests with that boundary.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_dd9f9b78-2dc7-49dc-b381-63e9c1061da4)

@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_f6c23623-ff23-4326-80b3-3095151d9d8e)

@cursor
cursor Bot temporarily deployed to deepsec-pr August 8, 2026 17:56 Inactive
…rics

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr August 8, 2026 17:59 Inactive
@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_e05c72e1-55c6-4eff-9ff4-f71c82244940)

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr August 8, 2026 18:06 Inactive
@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_f500015d-0402-46a4-87df-248606ea9bef)

Match the UPDATE trigger: retain already-public channels with
channel.update_settings alone.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr August 8, 2026 18:14 Inactive
@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_f4e7bf33-9d73-4f46-8636-f862228bc4e9)

Assert app_preview cannot create public channels, channel-admins cannot
flip private to public, and already-public channel edits stay allowed.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr August 8, 2026 18:43 Inactive
@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_3ad78ede-614c-4f16-9b86-1051f42596d9)

Use current_request_role()/is_internal_request_role() so anon API-key
and pgTAP callers are not skipped when session_user is postgres.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr August 8, 2026 18:50 Inactive
@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_db8f1c76-6f86-4b9d-9ce0-d229d5dd9799)

@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

Fixed in 67_test_public_channel_guard.sql (behavioral INSERT/UPDATE denial + already-public edit keep) and 70f8ddb03 (trigger uses current_request_role() so anon/API-key paths are enforced).

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
supabase/migrations/20260808175552_app_preview_public_channel_guard.sql (1)

62-79: 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Complete the required execution-plan validation before merge.

This row-level trigger runs once per affected public.channels row and calls rbac_check_permission_request during private-to-public transitions. Document execution frequency, roles, worst-case cardinalities, and indexes. Run EXPLAIN (ANALYZE, BUFFERS) against production-scale data and record the result.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@supabase/migrations/20260808175552_app_preview_public_channel_guard.sql`
around lines 62 - 79, Complete the execution-plan validation for
enforce_public_channel_app_settings_permission and its
rbac_check_permission_request call: document per-row trigger frequency, involved
roles, worst-case cardinalities, and required indexes; run EXPLAIN (ANALYZE,
BUFFERS) using production-scale data, and record the resulting plan and
performance findings with the migration or associated review documentation.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@supabase/migrations/20260808175552_app_preview_public_channel_guard.sql`:
- Around line 62-79: Complete the execution-plan validation for
enforce_public_channel_app_settings_permission and its
rbac_check_permission_request call: document per-row trigger frequency, involved
roles, worst-case cardinalities, and required indexes; run EXPLAIN (ANALYZE,
BUFFERS) using production-scale data, and record the resulting plan and
performance findings with the migration or associated review documentation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7006d6ea-08dc-4e2a-8202-be2aa86fd7b3

📥 Commits

Reviewing files that changed from the base of the PR and between bc2f9f8 and 70f8ddb.

📒 Files selected for processing (2)
  • supabase/migrations/20260808175552_app_preview_public_channel_guard.sql
  • supabase/tests/67_test_public_channel_guard.sql
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

@cursor
cursor Bot temporarily deployed to deepsec-pr August 8, 2026 18:56 Inactive
@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_46c52df9-9a47-40cc-9af1-a0f02e5760fb)

@sonarqubecloud

sonarqubecloud Bot commented Aug 8, 2026

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Confidence score: 4/5

  • In supabase/tests/67_test_public_channel_guard.sql, the private→public update path is not exercised with the App Preview key, so the PR’s stated App Preview denial behavior could regress unnoticed and allow unintended channel promotion in that context—add a dedicated App Preview-key case on the UPDATE transition to close the coverage gap.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="supabase/tests/67_test_public_channel_guard.sql">

<violation number="1" location="supabase/tests/67_test_public_channel_guard.sql:265">
P3: The PR explicitly lists "App Preview ... can no longer create or promote public/default channels" and "App Preview denial" as core coverage, but this test never exercises the App Preview key on the private→public UPDATE-promotion path. Test 3 uses the channel-admin key (lacking app.update_settings) to prove the BEFORE trigger denies promotion, so the trigger denial is covered in effect, but there is no direct assertion that the app_preview key itself is denied on an UPDATE flip — only the INSERT denial (test 1). Consider adding a preview-key UPDATE-public-flip throws_ok to match the stated coverage.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

'app_preview key can insert a private channel'
);

-- 3) Channel-admin cannot flip private -> public.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The PR explicitly lists "App Preview ... can no longer create or promote public/default channels" and "App Preview denial" as core coverage, but this test never exercises the App Preview key on the private→public UPDATE-promotion path. Test 3 uses the channel-admin key (lacking app.update_settings) to prove the BEFORE trigger denies promotion, so the trigger denial is covered in effect, but there is no direct assertion that the app_preview key itself is denied on an UPDATE flip — only the INSERT denial (test 1). Consider adding a preview-key UPDATE-public-flip throws_ok to match the stated coverage.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/tests/67_test_public_channel_guard.sql, line 265:

<comment>The PR explicitly lists "App Preview ... can no longer create or promote public/default channels" and "App Preview denial" as core coverage, but this test never exercises the App Preview key on the private→public UPDATE-promotion path. Test 3 uses the channel-admin key (lacking app.update_settings) to prove the BEFORE trigger denies promotion, so the trigger denial is covered in effect, but there is no direct assertion that the app_preview key itself is denied on an UPDATE flip — only the INSERT denial (test 1). Consider adding a preview-key UPDATE-public-flip throws_ok to match the stated coverage.</comment>

<file context>
@@ -0,0 +1,323 @@
+  'app_preview key can insert a private channel'
+);
+
+-- 3) Channel-admin cannot flip private -> public.
+SELECT tests.clear_authentication();
+SELECT set_config('request.jwt.claim.role', 'anon', true);
</file context>

@riderx
riderx merged commit fa0869e into main Aug 8, 2026
81 checks passed
@riderx
riderx deleted the codex/guard-app-preview-public-channel branch August 8, 2026 19:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants